/*
 *	ME2 C++ SDK
 *	Copyright (c) 2010 _silencer
 *
 *	Permission is hereby granted, free of charge, to any person
 *	obtaining a copy of this software and associated documentation
 *	files (the "Software"), to deal in the Software without
 *	restriction, including without limitation the rights to use,
 *	copy, modify, merge, publish, distribute, sublicense, and/or sell
 *	copies of the Software, and to permit persons to whom the
 *	Software is furnished to do so, subject to the following
 *	conditions:
 *
 *	The above copyright notice and this permission notice shall be
 *	included in all copies or substantial portions of the Software.
 *
 *	THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 *	EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 *	OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
 *	NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 *	HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 *	WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 *	FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 *	OTHER DEALINGS IN THE SOFTWARE.
 */

#include "stdafx.h"
#include "demo_mod.h"
#include "..\me2_sdk\unreal_game_Core.h"
#include "..\me2_sdk\unreal_game_Engine.h"

uengine_interface& engine_interface( uengine_interface::get_singleton() );
boost::shared_ptr<demo_mod> demo_mod_ptr;
volatile bool console_active( false );
volatile bool console_wait( false );
volatile bool console_waiting( false );

boost::mt19937 random_number_generator;
boost::uniform_real<float> scale_range( 0.1f, 5.0f );
boost::variate_generator<boost::mt19937&, boost::uniform_real<float> > get_random_scale( random_number_generator, scale_range );

void console_thread()
{
	using boost::lexical_cast;
	using boost::bad_lexical_cast;
	typedef std::vector<std::string> string_list;

	/* Wait until mod is up and running */
	while ( !console_active )
	{
		boost::system_time end_time( boost::get_system_time() + boost::posix_time::milliseconds( 100 ) );
		boost::thread::sleep( end_time );
	}

	/* Input loop */
	while ( console_active )
	{
		std::string line;

		/* Wait for command to be completed */
		console_waiting = console_wait;

		while ( console_wait )
		{
			boost::system_time end_time( boost::get_system_time() + boost::posix_time::milliseconds( 100 ) );
			boost::thread::sleep( end_time );
		}

		console_waiting = false;

		if ( !console_active || console_wait )
			continue;

		std::cout << "Demo > ";

		std::getline( std::cin, line );

		if ( !console_active || console_wait || ( line.size() < 1 ) )
		{
			std::cout << std::endl;
			continue;
		}

		line.resize( line.size() - 1 );
		boost::to_lower( line );

		string_list items;
		boost::split( items, line, boost::is_space() );

		string_list::iterator item( items.begin() );

		if ( item == items.end() )
			continue;

		std::string& cmd( *item++ );

		if ( ( cmd == "test" ) && ( item != items.end() ) )
		{
			std::string& arg0( *item++ );

			if ( arg0 == "resize" )
			{
				if ( demo_mod_ptr.get() )
					demo_mod_ptr->do_test_resize();
			}
			else
			if ( arg0 == "helmet" )
			{
				if ( demo_mod_ptr.get() )
					demo_mod_ptr->do_test_helmet();		
			}
		}
		else
		if ( cmd == "help" )
		{			
			std::cout << "Commands are:" << std::endl;
			std::cout << "	test + [resize, helmet]" << std::endl;
			std::cout << "Example:" << std::endl;
			std::cout << "	test resize" << std::endl;
		}
	}
}

void start_mod()
{
	/* Attach to console of launcher so we can show some information */
	AttachConsole( ATTACH_PARENT_PROCESS );

	*stdout = *_tfdopen( _open_osfhandle( (intptr_t) GetStdHandle(STD_OUTPUT_HANDLE), _O_WRONLY ), _T("a") ); 
	*stderr = *_tfdopen( _open_osfhandle( (intptr_t) GetStdHandle(STD_ERROR_HANDLE), _O_WRONLY ), _T("a") ); 
	*stdin = *_tfdopen( _open_osfhandle( (intptr_t) GetStdHandle(STD_INPUT_HANDLE), _O_WRONLY ), _T("r") ); 

	/* Now setup the sdk and get our mod running */
	std::cout << "demo_mod starting..." << std::endl;

	/* Launch new thread for console input */
	boost::thread console_thread( &console_thread );

	/* Make the console wait until all initialization messages are out */
	console_wait = true;

	/* Enable console now */
	console_active = true;

	/* Setup interface */
	engine_interface.initialize_interface();

	if ( engine_interface.is_initialized() )
		demo_mod_ptr.reset( new demo_mod() );
}

void stop_mod()
{
	/* Terminate console input */
	terminate_console();

	std::cout << "demo_mod stopping..." << std::endl;

	/* Cleanup */
	if ( demo_mod_ptr )
		demo_mod_ptr.reset();

	engine_interface.cleanup_interface();
}

demo_mod::demo_mod()
	: first_tick_( true ), first_render_( true ), do_test_resize_( false ), do_test_helmet_( false )
{
	std::cout << "demo_mod created." << std::endl;
}

demo_mod::~demo_mod()
{
	std::cout << "demo_mod is getting destroyed." << std::endl;
}

void demo_mod::on_tick( float delta_time )
{
	if ( first_tick_ )
	{
		std::cout << "demo_mod got its first tick with delta_time = " << delta_time << "." << std::endl;
		first_tick_ = false;

		/* First render has been shown? */
		console_wait = first_render_;

		return;
	}

	/* It is recommended to do engine interfacing only in calls coming from the engine, thread safety... */
	if ( do_test_resize_ )
	{
		scoped_get_console have_console;

		uobject* pawn = find_global_object<uobject>( "SFXPawn_Miranda BioH_Vixen_00.TheWorld.PersistentLevel.SFXPawn_Miranda" );
		ufunction* pawn_func = find_global_object<ufunction>( "Function BioPawn.SetScale" );

		if ( pawn->is_valid() && pawn_func->is_valid() )
		{
			float parms( get_random_scale() );
			std::cout << "demo_mod is calling BioPawn.SetScale( " << parms << " )." << std::endl;
			pawn->call_function( pawn_func, &parms, 0 );
		}
		else
		{
			std::cout << "demo_mod failed to locate the pawn or function (is Miranda inside the level?)." << std::endl;
		}

		do_test_resize_ = false;
	}

	if ( do_test_helmet_ )
	{
		scoped_get_console have_console;

		uobject* pawn = find_global_object<uobject>( "SFXPawn_Miranda BioH_Vixen_00.TheWorld.PersistentLevel.SFXPawn_Miranda" );
		ufunction* pawn_func = find_global_object<ufunction>( "Function SFXPawn_Henchman.ShowHelmet" );

		if ( pawn->is_valid() && pawn_func->is_valid() )
		{
			bool parms( ::rand() > 500 );
			std::cout << "demo_mod is calling SFXPawn_Henchman.ShowHelmet( " << ( parms ? "true" : "false" ) << " )." << std::endl;
			pawn->call_function( pawn_func, &parms, 0 );
		}
		else
		{
			std::cout << "demo_mod failed to locate the pawn or function (is Miranda inside the level?)." << std::endl;
		}

		do_test_helmet_ = false;
	}
}

void demo_mod::on_post_render( uobject* canvas )
{
	if ( first_render_ )
	{
		if ( canvas->is_valid() )
			std::cout << "demo_mod got its first post_render with canvas = " << canvas->get_full_name() << "." << std::endl;
		else
			std::cout << "demo_mod got its first post_render with canvas = none." << std::endl;

		first_render_ = false;

		/* First tick has been shown? */
		console_wait = first_tick_;

		return;
	}
}

void demo_mod::on_level_change()
{
	scoped_get_console have_console;

	std::cout << "demo_mod has detected a level change." << std::endl;
}
